package com.jeehentor.config.security.filter;

import com.alibaba.fastjson2.JSON;
import com.jeehentor.common.api.vo.LoginUser;
import com.jeehentor.common.vo.Result;
import com.jeehentor.config.security.handler.MyAuthenticationEntryPoint;
import com.jeehentor.utils.JWTUtil;
import com.jeehentor.utils.RedisUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static com.jeehentor.common.constant.CacheConstants.LOGIN_USER;


/**
 * 自定义jwt全局过滤器
 * <p>
 * 1.没有携带token放行
 * 2.携带token,将用户信息添加至security上下文中
 */
@Component
@RequiredArgsConstructor
public class JWTAuthenticationFilter extends OncePerRequestFilter {
    private static final Logger logger = Logger.getLogger(JWTAuthenticationFilter.class.toString());
    private final MyAuthenticationEntryPoint authenticationEntryPoint;
    private final RedisUtils redisUtils;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //否：获取请求头中携带的token
        String authorization = request.getHeader("Authorization");
//        logger.info("携带authorization:" + authorization);
        if (StringUtils.isBlank(authorization)) {
            // 兼容 EventSource
            authorization = request.getParameter("Authorization");
        }
        //判断是否携带token
        //否：放行，交给下一个过滤器
        if (StringUtils.isBlank(authorization)) {
            filterChain.doFilter(request, response);
            return;
        }

        String realToken = authorization.replace("Bearer ", "");
        if(StringUtils.isBlank(realToken)){
            filterChain.doFilter(request, response);
            return;
        }
        if (redisUtils.common.hasKey(realToken)) {
            filterChain.doFilter(request, response);
            return;
        }

        //是：校验jwt有效性
        Result<LoginUser> r = JWTUtil.verifyToken(realToken);
        if (!r.isSuccess()) {
            filterChain.doFilter(request, response);
            return;
        }
        LoginUser user = r.getResult();
        String username = user.getUsername();
        //认证
        //通过用户名，从redis中返回用户信息
        LoginUser userDetails = JSON.parseObject((String) redisUtils.getValueOperations().get(LOGIN_USER+username), LoginUser.class);
        if (ObjectUtils.isEmpty(userDetails)) {
            authenticationEntryPoint.commence(request, response, new AuthenticationServiceException("token无效"));
            return;
        }
        String newToken = JWTUtil.refreshIfNeeded(userDetails, 1000L * 60 * 60 * 24); // 剩不到一天刷新
        //自动刷新Token
        if (newToken != null) {
            // 更新Redis里的用户缓存（重新保存）
            redisUtils.getValueOperations().set(LOGIN_USER + username, JSON.toJSONString(userDetails), 7, TimeUnit.DAYS);
            // 给前端返回新的token
            response.setHeader("New-Token", newToken);
        }

        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
        // 新建 SecurityContext
        SecurityContext context = SecurityContextHolder.createEmptyContext();
        context.setAuthentication(usernamePasswordAuthenticationToken);
        SecurityContextHolder.setContext(context);

        filterChain.doFilter(request, response);
    }
}
